#include "pincfg.h"
#include "bsp_nand_w25n.h"
#include "str_ext.h"
#include "stdlib.h"
#include "lib_surport.h"
#include "bsp_qpi_opt.h"

nand_info_type bsp_snand_info;

//////////////////////////////////////////////////////////////////////////////////
// 状态描述：
// OPT-E  : 控制读写命名操作的是主存储区或者OPT存储区
// P_FAIL : 编程失败 - 编程地址无效或者区域受保护 - 执行编程或者执行RESET清零
// E_FAIL : 擦除失败 - 擦除地址无效或者区域受保护 - 执行擦除或者执行RESET清零
// WEL    : 写使能   - 1=enable, 0=disable, 执行编程或擦除指令时会自动清除WEL
// OIP    ：忙状态   - 读页，执行编程，擦除，reset后进入忙状态
//
// ECCS1---ECCS0---
// 0       0    ---No bit errors were detected during the previous read algorithm
// 0       1    ---Bit errors(<4) were detected and corrected
// 1       0    ---Bit errors greater than ECC capability(8 bits) and not corrected
// 1       1    ---Bit errors reach ECC capability( 8 bits) and not corrected
//////////////////////////////////////////////////////////////////////////////////

/*****************************************************************************
* @brief   FLASH INFO
*****************************************************************************/
/*
#define W25N_PAGE_MAIN_SIZE               2048
#define W25N_PAGE_SPARE_SIZE              64
#define W25N_PAGE_SIZE                    (W25N_PAGE_MAIN_SIZE + W25N_PAGE_SPARE_SIZE)
#define W25N_PAGE_PER_BLOCK               64
#define W25N_BLOCK_TOTAL                  1024
*/

/*****************************************************************************
* @brief   CMD LIST
*****************************************************************************/
#define NAND_CMD_WRITE_ENABLE 0x06
#define NAND_CMD_WRITE_DISABLE 0x04

#define NAND_CMD_FEATURE_GET 0x0F
#define NAND_CMD_FEATURE_SET 0x1F

#define NAND_CMD_READ_ID 0x9F

#define NAND_CMD_PAGE_READ 0x13		  //read page to cache
#define NAND_CMD_CACHE_READ 0x03	  //read data from cache
#define NAND_CMD_CACHE_READ_FAST 0x0B //read data from cache
#define NAND_CMD_CACHE_READ_X4 0x6B	  //read data from cache //cmd and addr is spi, data is qspi

#define NAND_CMD_PROGRAM_LOAD 0x02	  //写cache，未写的部分会被填充为0xFF
#define NAND_CMD_PROGRAM_LOAD_X4 0x32 //写cache，未写的部分会被填充为0xFF //cmd and addr is spi, data is qspi

#define NAND_CMD_PROGRAM_EXE 0x10 //将cache写入nand

#define NAND_CMD_PROGRAM_LOAD_RANDOM 0x84	 //写cache，未写的部分保持不变
#define NAND_CMD_PROGRAM_LOAD_RANDOM_X4 0x34 //写cache，未写的部分保持不变

#define NAND_CMD_BLOCK_ERASE 0xD8

#define NAND_CMD_RESET 0xFF

/*****************************************************************************
* @brief   hal
*****************************************************************************/
void hal_snand_spi_cs(uint8_t vol)
{
	if (vol)
	{
		nand_ss.en();
	}
	else
	{
		nand_ss.dis();
	}

	NOP4();
}

/*****************************************************************************
* @brief   auto cmd.
*****************************************************************************/
void bsp_snand_cmd_exe(uint8_t cmd)
{
	bsp_qpi_opt_mode(QSPI_OPERATE_MODE_111);

	hal_snand_spi_cs(0);
	bsp_qpi_cmd_exe(cmd);
	hal_snand_spi_cs(1);
}

//cmd with one par send define
void bsp_snand_cmd_set(uint8_t cmd, uint8_t par)
{
	bsp_qpi_opt_mode(QSPI_OPERATE_MODE_111);

	hal_snand_spi_cs(0);
	bsp_qpi_cmd_set(cmd, par);
	hal_snand_spi_cs(1);
}

uint8_t bsp_snand_cmd_get(uint8_t cmd)
{
	uint8_t rd;

	bsp_qpi_opt_mode(QSPI_OPERATE_MODE_111);

	hal_snand_spi_cs(0);
	rd = bsp_qpi_cmd_get(cmd);
	hal_snand_spi_cs(1);

	return rd;
}

void bsp_snand_cmd_aset(uint8_t cmd, uint8_t addr, uint8_t par)
{
	bsp_qpi_opt_mode(QSPI_OPERATE_MODE_111);

	hal_snand_spi_cs(0);
	bsp_qpi_cmd_aset(cmd, addr, par);
	hal_snand_spi_cs(1);
}

uint8_t bsp_snand_cmd_aget(uint8_t cmd, uint8_t addr)
{
	uint8_t rd;

	bsp_qpi_opt_mode(QSPI_OPERATE_MODE_111);

	hal_snand_spi_cs(0);
	rd = bsp_qpi_cmd_aget(cmd, addr);
	hal_snand_spi_cs(1);

	return rd;
}

void bsp_snand_cmd_auto_set(uint8_t cmd, const uint8_t *buff, uint8_t cnt)
{
	bsp_qpi_opt_mode(QSPI_OPERATE_MODE_111);
	hal_snand_spi_cs(0);
	bsp_qpi_cmd_multi_write(cmd, 0, QSPI_CMD_ADRLEN_0_BYTE, buff, cnt);
	hal_snand_spi_cs(1);
}
void bsp_snand_cmd_auto_get(uint8_t cmd, uint8_t *buff, uint8_t cnt)
{
	bsp_qpi_opt_mode(QSPI_OPERATE_MODE_111);
	hal_snand_spi_cs(0);
	bsp_qpi_cmd_multi_read(cmd, 0, QSPI_CMD_ADRLEN_0_BYTE, buff, cnt);
	hal_snand_spi_cs(1);
}

/*-------------------------------------------------------------*
* write enable
*--------------------------------------------------------------*/
void bsp_snand_reset(void)
{
	bsp_snand_cmd_exe(NAND_CMD_RESET);

	bsp_snand_wait_ready();
}

/*-------------------------------------------------------------*
*  featute get
*  read twice to confirm
*--------------------------------------------------------------*/
uint8_t bsp_snand_featute_get(uint8_t addr)
{
	uint8_t st1, st2;
	char rd_cnt = 5;

	while (rd_cnt--)
	{
		st1 = bsp_snand_cmd_aget(NAND_CMD_FEATURE_GET, addr);
		st2 = bsp_snand_cmd_aget(NAND_CMD_FEATURE_GET, addr);

		/*confirm*/
		if (st2 == st1)
			break;
	}

	return st1;
}

/*-------------------------------------------------------------*
*  featute set
*--------------------------------------------------------------*/
uint8_t bsp_snand_featute_set(uint8_t addr, uint8_t msk, uint8_t dat)
{
	uint8_t st_rd, st_wr, cnt;

	cnt = 5;

	dat &= msk;

	// 读取原数据，如果数据已匹配直接退出
	st_wr = bsp_snand_featute_get(addr);
	if ((st_wr & msk) == dat)
		return st_wr;

	// 设置
	st_wr &= ~msk;
	st_wr |= dat;

	while (cnt--)
	{
		bsp_snand_cmd_aset(NAND_CMD_FEATURE_SET, addr, st_wr);

		bsp_snand_wait_ready();

		// 读回验证
		st_rd = bsp_snand_featute_get(addr);

		if ((st_rd & msk) == dat)
		{
			break;
		}
	}

	return st_rd;
}

/*-------------------------------------------------------------*
*  wait for chip ready
*--------------------------------------------------------------*/
uint8_t bsp_snand_wait_ready(void)
{
	uint16_t tick_start, tick_used;

	tick_start = lib_tick_get();

	while (1)
	{
		uint8_t st;
		st = bsp_snand_featute_get(NAND_FEATURE_3_ST);

		bsp_snand_info.dev_st = st;

		if (0 == (st & NAND_ST_BUSY_MASK))
		{
			break;
		}

		//对于W25N，最大实际忙时为5ms
		tick_used = lib_tick_get() - tick_start;
		if (tick_used > 50)
			return 1;
	}

	return 0;
}

/*-------------------------------------------------------------*
*  wait for chip ready
*--------------------------------------------------------------*/
uint8_t bsp_snand_ecc_error_get(void)
{
	return bsp_snand_info.ecc_err;
}

/*-------------------------------------------------------------*
* write enable
*--------------------------------------------------------------*/
uint8_t bsp_snand_write_enable(void)
{
	uint8_t set_cnt = 0;
	uint8_t st;

	for (;;)
	{
		bsp_snand_cmd_exe(NAND_CMD_WRITE_ENABLE);

		/*get status*/
		st = bsp_snand_featute_get(NAND_FEATURE_3_ST);

		/*write enable latch judge*/
		if (st & NAND_ST_WREN_MASK)
		{
			break;
		}

		set_cnt++;
		if (set_cnt > 3)
		{
			return 1;
		}
	}

	return 0;
}

/*-------------------------------------------------------------*
* write enable
*--------------------------------------------------------------*/
uint8_t bsp_snand_write_disable(void)
{
	uint8_t set_cnt = 0;
	uint8_t st;

	for (;;)
	{
		bsp_snand_cmd_exe(NAND_CMD_WRITE_DISABLE);

		/*get status*/
		st = bsp_snand_featute_get(NAND_FEATURE_3_ST);

		/*write enable latch judge*/
		if (0 == (st & NAND_ST_WREN_MASK))
		{
			break;
		}

		set_cnt++;
		if (set_cnt > 3)
		{
			return 1;
		}
	}

	return 0;
}

/*-------------------------------------------------------------*
*  read id
*--------------------------------------------------------------*/
void bsp_snand_read_id(uint8_t *dat)
{
	bsp_qpi_opt_mode(QSPI_OPERATE_MODE_111);

	hal_snand_spi_cs(0);
	bsp_qpi_cmd_multi_read(NAND_CMD_READ_ID, 0, QSPI_CMD_ADRLEN_0_BYTE, dat, 4);
	hal_snand_spi_cs(1);
}

/*-------------------------------------------------------------*
*  read page to cache
*--------------------------------------------------------------*/
uint8_t bsp_snand_read_page(uint32_t addr)
{
	uint8_t buff[8], cnt = 2;

	while (cnt--)
	{
		bsp_qpi_opt_mode(QSPI_OPERATE_MODE_111);

		hal_snand_spi_cs(0);
		bsp_qpi_cmd_multi_write(NAND_CMD_PAGE_READ, addr, QSPI_CMD_ADRLEN_3_BYTE, 0, 0);
		hal_snand_spi_cs(1);

		bsp_snand_wait_ready();

		//[5:4]=10表示有错误且无法修复
		if (bsp_snand_info.dev_st & 0x20)
		{
			bsp_snand_info.ecc_err = 1;
		}
		else
		{
			bsp_snand_info.ecc_err = 0;
			break;
		}
	}

	return 0;
}

/*-------------------------------------------------------------*
*  read page from cache
*--------------------------------------------------------------*/
uint8_t bsp_snand_read_cache(uint16_t addr, uint8_t *dat, uint16_t Len)
{
	bsp_qpi_opt_mode(QSPI_OPERATE_MODE_111);

	hal_snand_spi_cs(0);

	bsp_qpi_addr_read(
		NAND_CMD_CACHE_READ,
		addr, QSPI_CMD_ADRLEN_2_BYTE,
		8, //dummy
		dat, Len);

	hal_snand_spi_cs(1);

	return 0;
}

/*-------------------------------------------------------------*
*  read page from cache
*--------------------------------------------------------------*/
uint8_t bsp_snand_q_read_cache(uint16_t addr, uint8_t *dat, uint16_t Len)
{
	uint8_t buff[8];

	bsp_qpi_opt_mode(QSPI_OPERATE_MODE_114);

	hal_snand_spi_cs(0);

	bsp_qpi_addr_read(
		NAND_CMD_CACHE_READ_X4,
		addr, QSPI_CMD_ADRLEN_2_BYTE,
		8, //dummy
		dat, Len);

	hal_snand_spi_cs(1);

	return 0;
}

/*-------------------------------------------------------------*
*  write page to cache
*--------------------------------------------------------------*/
uint8_t bsp_snand_write_cache(uint16_t addr, const uint8_t *dat, uint16_t Len)
{
	bsp_snand_write_enable();

	bsp_qpi_opt_mode(QSPI_OPERATE_MODE_111);

	hal_snand_spi_cs(0);

	bsp_qpi_addr_write(
		NAND_CMD_PROGRAM_LOAD,
		addr, QSPI_CMD_ADRLEN_2_BYTE,
		(uint8_t *)dat, Len);

	hal_snand_spi_cs(1);

	return 0;
}

/*-------------------------------------------------------------*
*  write page to cache
*--------------------------------------------------------------*/
uint8_t bsp_snand_random_write_cache(uint16_t addr, const uint8_t *dat, uint16_t Len)
{
	bsp_snand_write_enable();

	bsp_qpi_opt_mode(QSPI_OPERATE_MODE_111);

	hal_snand_spi_cs(0);

	bsp_qpi_addr_write(
		NAND_CMD_PROGRAM_LOAD_RANDOM,
		addr, QSPI_CMD_ADRLEN_2_BYTE,
		(uint8_t *)dat, Len);

	hal_snand_spi_cs(1);

	return 0;
}

/*-------------------------------------------------------------*
*  write page to cache
*--------------------------------------------------------------*/
uint8_t bsp_snand_q_write_cache(uint16_t addr, const uint8_t *dat, uint16_t Len)
{
	bsp_snand_write_enable();

	bsp_qpi_opt_mode(QSPI_OPERATE_MODE_114);

	hal_snand_spi_cs(0);

	bsp_qpi_addr_write(
		NAND_CMD_PROGRAM_LOAD_X4,
		addr, QSPI_CMD_ADRLEN_2_BYTE,
		(uint8_t *)dat, Len);

	hal_snand_spi_cs(1);

	return 0;
}

/*-------------------------------------------------------------*
*  write page to cache
*--------------------------------------------------------------*/
uint8_t bsp_snand_q_random_write_cache(uint16_t addr, const uint8_t *dat, uint16_t Len)
{
	bsp_snand_write_enable();

	bsp_qpi_opt_mode(QSPI_OPERATE_MODE_114);

	hal_snand_spi_cs(0);

	bsp_qpi_addr_write(
		NAND_CMD_PROGRAM_LOAD_RANDOM_X4,
		addr, QSPI_CMD_ADRLEN_2_BYTE,
		(uint8_t *)dat, Len);

	hal_snand_spi_cs(1);

	return 0;
}

/*-------------------------------------------------------------*
*  program exe
*--------------------------------------------------------------*/
uint8_t bsp_snand_program_exe(uint32_t addr)
{
	bsp_snand_write_enable();

	bsp_qpi_opt_mode(QSPI_OPERATE_MODE_111);

	hal_snand_spi_cs(0);
	bsp_qpi_cmd_multi_write(NAND_CMD_PROGRAM_EXE, addr, QSPI_CMD_ADRLEN_3_BYTE, 0, 0);
	hal_snand_spi_cs(1);

	bsp_snand_wait_ready();

	return 0;
}

/*-------------------------------------------------------------*
*  program exe
*--------------------------------------------------------------*/
uint8_t bsp_snand_block_erase(uint32_t block_addr)
{
	uint8_t buff[8];

	uint32_t addr;

	addr = block_addr * bsp_snand_info.page_per_block; //指令需提供页地址

	bsp_snand_write_enable();

	bsp_qpi_opt_mode(QSPI_OPERATE_MODE_111);

	hal_snand_spi_cs(0);
	bsp_qpi_cmd_multi_write(NAND_CMD_BLOCK_ERASE, addr, QSPI_CMD_ADRLEN_3_BYTE, 0, 0);
	hal_snand_spi_cs(1);

	bsp_snand_wait_ready();

	return 0;
}

/*-------------------------------------------------------------*
*  program exe
*--------------------------------------------------------------*/
uint8_t bsp_snand_chip_erase(void)
{
	uint16_t index;
	for (index = 0; index < bsp_snand_info.block_total; index++)
	{
		bsp_snand_block_erase(index);
	}

	return 0;
}

/*-------------------------------------------------------------*
*  program exe
*--------------------------------------------------------------*/
uint8_t bsp_snand_block_empty_check(uint32_t block_addr)
{
	uint8_t *buff, dpg = 0;
	uint32_t page, page_base;
	uint16_t page_size;

	page_size = bsp_snand_info.page_main_size + bsp_snand_info.page_spare_size;

	buff = malloc(page_size + 16);

	page_base = block_addr * bsp_snand_info.page_per_block;

	for (page = 0; page < bsp_snand_info.page_per_block; page++)
	{
		bsp_snand_read_page(page_base + page_base);
		bsp_snand_read_cache(0, buff, page_size);

		if (comp_mem_dat_u32(buff, UINT32_MAX, (page_size) / 4))
		{
			dpg = page + 1;
			break;
		}
	}

	free(buff);

	return dpg;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*-------------------------------------------------------------*
*  W25N SPECIAL API
*--------------------------------------------------------------*/
uint8_t bsp_w25n_opt_mem_select(void)
{
	bsp_snand_featute_set(NAND_FEATURE_2_CFG, 0x58, 0x58);

	return 0;
}

uint8_t bsp_w25n_main_mem_select(void)
{
	bsp_snand_featute_set(NAND_FEATURE_2_CFG, 0x58, 0x18);

	return 0;
}

uint8_t bsp_w25n_init(void)
{
	bsp_snand_reset();

	bsp_snand_wait_ready();

	//开启 ECC 和 完整BUFF
	bsp_snand_featute_set(NAND_FEATURE_2_CFG, 0x58, 0x18);

	return 0;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*-------------------------------------------------------------*
*  GD5 SPECIAL API
*--------------------------------------------------------------*/
uint8_t bsp_gd5_opt_mem_select(void)
{
	bsp_snand_featute_set(NAND_FEATURE_2_CFG, 0x51, 0x51);
	return 0;
}

uint8_t bsp_gd5_main_mem_select(void)
{
	bsp_snand_featute_set(NAND_FEATURE_2_CFG, 0x51, 0x11);

	return 0;
}

uint8_t bsp_gd5_init(void)
{
	bsp_snand_reset();

	bsp_snand_wait_ready();

	bsp_gd5_main_mem_select();

	return 0;
}
